home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 43 / Amiga Format CD43 (1999)(Future Publishing)(GB)(Track 1 of 2)[!][issue 1999-09].iso / -serious- / misc / bhformat / main.c < prev    next >
C/C++ Source or Header  |  1999-06-15  |  28KB  |  998 lines

  1. /*
  2.  *  Format -- disk formatting and file-system creation program
  3.  *  Copyright (C) 1999 Ben Hutchings
  4.  
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with this program; if not, write to the Free Software
  17.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  */
  19.  
  20. /*
  21.  *  This is intended to be a compatible replacement for Format version
  22.  *  40 with important additions - in particular, it is safe to use
  23.  *  with drives larger than 4 GB.  It can also warn about several
  24.  *  problems with large drives and old OS versions.  Currently its
  25.  *  main deficiency is a lack of localisation.  It will also fail
  26.  *  currently if started from Workbench with no arguments, because I
  27.  *  haven't implemented the device selection interface that should be
  28.  *  used in that case.  It also needs a good deal of testing since
  29.  *  I've had to completely restructure the program completely in order
  30.  *  to implement the GUI.
  31.  *
  32.  *  Please send bug reports, success reports, fixes and improvements
  33.  *  to me.  I really need help with localisation.  To start with, I
  34.  *  want to be able to read strings out of the system catalog for
  35.  *  messages that are the same as in Format v40.  Then I'll have to
  36.  *  define additional message strings for the extensions.
  37.  *
  38.  *  - Ben Hutchings <womble@zzumbouk.demon.co.uk>
  39.  *
  40.  *  Version 43.4, 9 June 1999.
  41.  *
  42.  *  Fixed 2 medium and 4 minor bugs:
  43.  *
  44.  *  - In the GUI, the logic of the trashcan check-box was inverted.
  45.  *  - Starting Format from Workbench with the icon of an ejected disk
  46.  *    would result in formatting whatever was currently in the drive
  47.  *    that the ejected volume was previously in.  It should, and now
  48.  *    does, ask for the ejected disk to be re-inserted and fail if
  49.  *    the user refuses.
  50.  *
  51.  *  - The warning about MaxTransfer setting had several words repeated.
  52.  *  - The default disk icon for a file-system type would not be fetched
  53.  *    unless a file-system type was specified using the CLI DOSTYPE
  54.  *    keyword.
  55.  *  - The area cleared after formatting the disk and before displaying
  56.  *    "Initializing disk..." was calculated wrongly (typo).
  57.  *  - The positions of the ticks on the format progress bar were
  58.  *    calculated slightly wrongly.
  59.  *
  60.  *  Version 43.3, 6 June 1999.
  61.  *
  62.  *  Changed all variable and function names to Hungarian naming
  63.  *  convention (the real thing, not Micro$oft's botched version).
  64.  *  Hopefully this makes the code easy to understand.
  65.  *
  66.  *  Restructured the program so that the GUI and CLI procedures can
  67.  *  share as much code as possible.  Changed error reporting code to
  68.  *  use a single function that will print the message to the console
  69.  *  or display it in a requester depending on how the program was
  70.  *  started.
  71.  *
  72.  *  Wrote almost all the necessary GUI code (search for TODO: in gui.c
  73.  *  to find the missing bits).  The GUI is, of course, font-sensitive.
  74.  *
  75.  *  Added another rule for finding default disk icons - if there isn't
  76.  *  a default icon for the device then the program looks for a default
  77.  *  icon for the file-system type before finally asking icon.library
  78.  *  for the default disk icon.  This should now match DefIcons rules
  79.  *  exactly.
  80.  *
  81.  *  Version 43.2, 25 April 1999.
  82.  *
  83.  *  Added DOSTYPE keyword for specifying file-system type to handlers
  84.  *  that implement multiple non-native types.  Added FLAGS keyword for
  85.  *  specifying non-standard flags when opening the device driver.
  86.  *  Added NOVERIFY switch for disabling format verification (pointless).
  87.  *  Added DISKICON switch for putting a default disk icon on the disk.
  88.  *  Marked comments on unimplemented stuff with "TODO" so that they're
  89.  *  easy to find.
  90.  *
  91.  *  Version 43.1, 13 April 1999.
  92.  *
  93.  *  The product of some hours' hacking last year, and a little
  94.  *  clean-up recently.  */
  95.  
  96.  
  97. #include <clib/dos_protos.h>
  98. #include <clib/exec_protos.h>
  99. #include <clib/icon_protos.h>
  100. #include <clib/intuition_protos.h>
  101. #include <devices/newstyle.h>
  102. #include <devices/trackdisk.h>
  103. #include <dos/dos.h>
  104. #include <dos/dosextens.h>
  105. #include <dos/filehandler.h>
  106. #include <exec/devices.h>
  107. #include <exec/errors.h>
  108. #include <exec/io.h>
  109. #include <exec/libraries.h>
  110. #include <exec/memory.h>
  111. #include <exec/resident.h>
  112. #include <graphics/gfxbase.h>
  113. #include <intuition/intuition.h>
  114. #include <workbench/workbench.h>
  115. #include <stdlib.h>
  116. #include <string.h>
  117.  
  118. #ifdef NDEBUG
  119. #include <inline/dos.h>
  120. #include <inline/exec.h>
  121. #include <inline/icon.h>
  122. #include <inline/intuition.h>
  123. /* These calls require too many address registers to be compilable */
  124. #undef EasyRequest
  125. #undef EasyRequestArgs
  126. #endif
  127.  
  128. #include "format.h"
  129.  
  130.  
  131. static void WordWrapSz( char * psz );
  132. static BOOL bTransferCylinder(
  133.     APTR pBuffer, ULONG icyl, UWORD cmd64, UWORD cmd32 );
  134.  
  135. char szDosDevice[MAX_FS_NAME_LEN+2];
  136. char * pchDosDeviceColon;
  137. struct DosEnvec * pdenDevice;
  138. struct DosList * pdlList, * pdlDevice;
  139. ULLONG ibyStart, ibyEnd;
  140.  
  141. static char szVolume[MAX_FS_NAME_LEN+2] __attribute__((aligned (4)));
  142. static char * pszExecDevice;
  143. static ULONG fstCurrent;
  144. static BOOL bFstSet;
  145. static ULONG devfCurrent;
  146. static BOOL bDevfSet;
  147. static struct FileSysStartupMsg * pfssm;
  148. static ULLONG cbyTrack, cbyCylinder;
  149. static struct MsgPort * pmpDiskIO;
  150. static struct IOStdReq * piosDisk;
  151. static BOOL bExecDevOpen;
  152. static BOOL bSuspectIDE;
  153. static BOOL bInhibited;
  154. static ULONG * paulWriteBuffer, * paulReadBuffer;
  155. static ULONG cbyTransfer;
  156.  
  157.  
  158. static const char szVersion[] = "$VER: BHFormat 43.4 (" VER_DATE ")\r\n";
  159.  
  160.  
  161. int main(void)
  162. {
  163.     int rc = _WBenchMsg ? rcGuiMain() : rcCliMain();
  164.     FreeAll();
  165.     return rc;
  166. }
  167.  
  168.  
  169. void ReportErrSz( ErrorType ert, LONG err, const char * pszMessage, ... )
  170. {
  171.     static const char * const apszErrorTypes[] = {"Warning","Error","Failure"};
  172.     char szSysMessage[FAULT_MAX];
  173.     char szFormat[256];
  174.     static char szOutput[1024];
  175.  
  176.     szSysMessage[0] = 0;
  177.     if( err != -1 )
  178.     (void) Fault( err ? err : IoErr(),
  179.               (_WBenchMsg && !pszMessage) ? 0 : "",
  180.               szSysMessage, FAULT_MAX );
  181.  
  182.     strcpy( szFormat, "Format " );
  183.     strcat( szFormat, apszErrorTypes[ert] );
  184.  
  185.     if(!_WBenchMsg)
  186.     {
  187.     if(pszMessage)
  188.     {
  189.         strcat( szFormat, " - " );
  190.         strcat( szFormat, pszMessage );
  191.     }
  192.     strcat( szFormat, szSysMessage );
  193.     strcat( szFormat, "\n" );
  194.     RawDoVFmtSz( szOutput, szFormat, (APTR)(&pszMessage+1) );
  195.     WordWrapSz(szOutput);
  196.     (void) FPuts( bpfhStdErr, szOutput );
  197.     }
  198.     else if(IntuitionBase)
  199.     {
  200.     struct EasyStruct es;
  201.  
  202.     char * pszBodyFormat = szFormat + strlen(szFormat) + 1;
  203.     *pszBodyFormat = 0;
  204.     if(pszMessage)
  205.         strcpy( pszBodyFormat, pszMessage );
  206.     strcat( pszBodyFormat, szSysMessage );
  207.  
  208.     es.es_StructSize = sizeof(struct EasyStruct);
  209.     es.es_Flags = 0;
  210.     es.es_Title = szFormat;
  211.     es.es_TextFormat = "%s";
  212.     es.es_GadgetFormat = (ert == ertFailure) ? "Cancel" : "OK";
  213.     RawDoVFmtSz( szOutput, pszBodyFormat, (APTR)(&pszMessage+1) );
  214.     WordWrapSz(szOutput);
  215.     (void) EasyRequest( 0, &es, 0, (ULONG)szOutput );
  216.     }
  217.     /* else we can't report the error */
  218. }
  219.  
  220. static void WordWrapSz( char * psz )
  221. {
  222.     char * pszLine, * pchSpace, * pchLastSpace;
  223.     pszLine = psz;
  224.     pchLastSpace = pszLine-1;
  225.     while( (pchSpace = strchr( pchLastSpace+1, ' ' )) != 0 )
  226.     {
  227.     size_t cch = pchSpace - pszLine;
  228.  
  229.     if( cch >= 60 )
  230.     {
  231.         if( cch == 60 || pchLastSpace < pszLine )
  232.         pchLastSpace = pchSpace;
  233.  
  234.         *pchLastSpace = '\n';
  235.         pszLine = pchLastSpace+1;
  236.     }
  237.  
  238.     pchLastSpace = pchSpace;
  239.     }
  240. }
  241.  
  242.  
  243. BOOL bSetSzDosDeviceFromSz( const char * pszDevice )
  244. {
  245.     /* Check the length of the device name - it may be followed by a
  246.        colon, and possibly some junk which we must ignore */
  247.     char * pszEnd = strchr( pszDevice, ':' );
  248.     size_t cch = pszEnd ? (pszEnd - pszDevice) : strlen(pszDevice);
  249.  
  250.     if( cch != 0 && cch <= MAX_FS_NAME_LEN )
  251.     {
  252.     /* Make a copy, without the colon */
  253.     strncpy( szDosDevice, pszDevice, cch );
  254.     pchDosDeviceColon = &szDosDevice[cch]; /* need to put it back later */
  255.     *pchDosDeviceColon = 0;
  256.     *(pchDosDeviceColon+1) = 0;
  257.     return TRUE;
  258.     }
  259.  
  260.     ReportErrSz( ertError, ERROR_INVALID_COMPONENT_NAME, 0 );
  261.     return FALSE;
  262. }
  263.  
  264.  
  265. BOOL bSetSzVolumeFromSz( const char * pszVolume )
  266. {
  267.     /* Check the length and validity of the volume name */
  268.     size_t cch = strlen(pszVolume);
  269.  
  270.     if( cch != 0 && cch <= MAX_FS_NAME_LEN && strpbrk( pszVolume, ":/" ) == 0 )
  271.     {
  272.     /* Make a copy with a length prefix because v36 Format function
  273.        incorrectly expects a BSTR */
  274.     szVolume[0] = cch;
  275.     strcpy( &szVolume[1], pszVolume );
  276.     return TRUE;
  277.     }
  278.  
  279.     ReportErrSz( ertError, ERROR_INVALID_COMPONENT_NAME, 0 );
  280.     return FALSE;
  281. }
  282.  
  283.  
  284. BOOL bSetFstFromSz( const char * pszFileSysType )
  285. {
  286.     char * pszEnd;
  287.     ULONG ub;
  288.     size_t cub;
  289.  
  290.     if( *pszFileSysType == 0 )
  291.     {
  292.     bFstSet = FALSE;
  293.     return TRUE;
  294.     }
  295.  
  296.     /* Look for a C-style number as used in Mountlists. */
  297.     fstCurrent = strtoul( pszFileSysType, &pszEnd, 0 );
  298.     if( *pszEnd == 0 )
  299.     {
  300.     bFstSet = TRUE;
  301.     return TRUE;
  302.     }
  303.  
  304.     /* Look for a type in the standard-ish format of text with
  305.        \-escaped numbers. These numbers normally have only one
  306.        digit, so it's a bit hard to decide what base they are
  307.        in.  I have specified 8 here because that's what C uses
  308.        for \-escaped character numbers. */
  309.     cub = 0;
  310.     fstCurrent = 0;
  311.     while( (ub = (unsigned char)*pszFileSysType++) != 0 )
  312.     {
  313.     if( ub == (unsigned char)'\\' )
  314.     {
  315.         ub = strtoul( pszFileSysType, (char **)&pszFileSysType, 8 );
  316.         if( ub >= 0x100 )
  317.         {
  318.         cub = 0;
  319.         break;
  320.         }
  321.     }
  322.     fstCurrent = (fstCurrent << 8) + ub;
  323.     ++cub;
  324.     }
  325.     if( cub == 4 )
  326.     {
  327.     bFstSet = TRUE;
  328.     return TRUE;
  329.     }
  330.  
  331.     ReportErrSz( ertError, -1, "Invalid DosType specification" );
  332.     return FALSE;
  333. }
  334.  
  335.  
  336. BOOL bSetDevfFromSz( const char * pszDevFlags )
  337. {
  338.     if( *pszDevFlags == 0 )
  339.     {
  340.     bDevfSet = FALSE;
  341.     return TRUE;
  342.     }
  343.  
  344.     devfCurrent = strtoul( pszDevFlags, (char **)&pszDevFlags, 0 );
  345.     if( *pszDevFlags == 0 )
  346.     {
  347.     bDevfSet = TRUE;
  348.     return TRUE;
  349.     }
  350.  
  351.     ReportErrSz( ertError, ERROR_BAD_NUMBER, 0 );
  352.     return FALSE;
  353. }
  354.  
  355.  
  356. BOOL bGetDosDevice(void)
  357. {
  358.     if(!pdlList)
  359.     {
  360.     pdlList = LockDosList( LDF_DEVICES | LDF_READ );
  361.     D(Printf( "LockDosList( LDF_DEVICES | LDF_READ ) = 0x%08lx\n",
  362.           (ULONG)pdlList ));
  363.     *pchDosDeviceColon = 0;
  364.     pdlDevice = FindDosEntry( pdlList, szDosDevice, LDF_DEVICES );
  365.     D(Printf( "FindDosEntry( 0x%08lx, \"%s\", LDF_DEVICES ) = 0x%08lx\n",
  366.           (ULONG)pdlList, (ULONG)szDosDevice, (ULONG)pdlDevice ));
  367.     if( pdlDevice == 0 )
  368.     {
  369.         ReportErrSz( ertError, ERROR_DEVICE_NOT_MOUNTED, 0 );
  370.         return FALSE;
  371.     }
  372.     }
  373.  
  374.     /* Find startup message and verify file-system settings. Use
  375.        TypeOfMem to protect against devices that use integer or string
  376.        startup values. */
  377.     if( (pfssm = (struct FileSysStartupMsg *)
  378.      BADDR(pdlDevice->dol_misc.dol_handler.dol_Startup)) == 0
  379.     || TypeOfMem(pfssm) == 0
  380.     || pfssm->fssm_Device == 0
  381.     || (pdenDevice = (struct DosEnvec *)BADDR(pfssm->fssm_Environ)) == 0
  382.     || TypeOfMem(pdenDevice) == 0
  383.     || pdenDevice->de_TableSize < DE_DOSTYPE
  384.     /* Check that parameters that should always be 0, are */
  385.     || pdenDevice->de_SecOrg != 0
  386.     || pdenDevice->de_Interleave != 0 )
  387.     {
  388.     ReportErrSz( ertError, ERROR_OBJECT_WRONG_TYPE, 0 );
  389.     return FALSE;
  390.     }
  391.  
  392.     /* Get the device name with the original correct case */
  393.     RawDoFmtSz( szDosDevice, "%b", pdlDevice->dol_Name );
  394.  
  395.     /* Unlike most BCPL strings, this one is guaranteed to be null-
  396.        terminated. */
  397.     pszExecDevice = (char *)BADDR(pfssm->fssm_Device) + 1;
  398.  
  399.     /* If the device has a native Amiga file-system, we can check for
  400.        various limitations and also apply the various command-line
  401.        flags that specify which variant to use for the new volume. */
  402.        
  403.     if( pdenDevice->de_DosType >= 0x444F5300
  404.     && pdenDevice->de_DosType <= 0x444F5305 )
  405.     {
  406.     const struct Resident * prt;
  407.     UWORD verFS;
  408.  
  409.     /* I'd prefer to do this properly by reading the segment size
  410.        and going through the segment list, but the fake ROM
  411.        "segment lists" don't have valid segment sizes set so that
  412.        would be a bit pointless. Perhaps I should use TypeOfMem
  413.        to decide whether the segment is in ROM or RAM first? */
  414.  
  415.     prt = (struct Resident *)((char *)BADDR(
  416.         pdlDevice->dol_misc.dol_handler.dol_SegList) + 4);
  417.     while( prt->rt_MatchWord != RTC_MATCHWORD
  418.            || prt->rt_MatchTag != prt )
  419.         prt = (struct Resident *)((UWORD *)prt + 1);
  420.     verFS = prt->rt_Version;
  421.     D(Printf( "found RomTag at 0x%08lx; rt_Version = %lu\n",
  422.           (ULONG)prt, (ULONG)verFS ));
  423.         
  424.     /* check that the fs can handle this device correctly */
  425.  
  426.     cbyTrack = (ULLONG)pdenDevice->de_BlocksPerTrack
  427.         * (ULLONG)(pdenDevice->de_SizeBlock * sizeof(LONG));
  428.     cbyCylinder = cbyTrack * pdenDevice->de_Surfaces;
  429.     ibyStart = pdenDevice->de_LowCyl * cbyCylinder;
  430.     ibyEnd = (pdenDevice->de_HighCyl + 1) * cbyCylinder;
  431.  
  432.     if( ibyEnd - ibyStart > 0x100000000ULL
  433.         && verFS <= 43 ) /* perhaps v44 will change this? ;-) */
  434.     {
  435.         ReportErrSz(
  436.         ertError,
  437.         -1,
  438.         "This device is larger than 4 gigabytes in size. The"
  439.         " Amiga file-system cannot handle a device this large." );
  440.         return FALSE;
  441.     }
  442.     if( ibyEnd > 0x100000000ULL && verFS < 43 )
  443.     {
  444.         ReportErrSz(
  445.         ertError,
  446.         -1,
  447.         "This device extends beyond the first 4 gigabytes of the"
  448.         " disk it is on.  In order to use it, you must install"
  449.         " version 43 or later of the Amiga file-system." );
  450.         return FALSE;
  451.     }
  452.  
  453.     if( ibyEnd - ibyStart > 0x80000000ULL && verFS < 40 )
  454.     {
  455.         ReportErrSz(
  456.         ertError,
  457.         -1,
  458.         "This device is larger than 2 gigabytes in size. In order to"
  459.         " use it, you must install version 40 or later of the Amiga"
  460.         " file-system." );
  461.         return FALSE;
  462.     }
  463.     }
  464.  
  465.     /* Certain documentation says MEMF_PUBLIC doesn't matter, and
  466.        certain progams don't bother setting it by default.  They
  467.        are wrong and should be fixed! */
  468.     if( !(pdenDevice->de_BufMemType & MEMF_PUBLIC) )
  469.     {
  470.     ReportErrSz(
  471.         ertWarning,
  472.         -1,
  473.         "This device is set to use buffers in memory that may not be"
  474.         " \"public\". This can cause your computer to crash if you use a"
  475.         " virtual memory system. You should add 1 to the BufMemType"
  476.         " setting for this device to avoid this." );
  477.     pdenDevice->de_BufMemType |= MEMF_PUBLIC;
  478.     }
  479.  
  480.     return TRUE;
  481. }
  482.  
  483.  
  484. void FreeDosDevice(void)
  485. {
  486.     if(pdlList)
  487.     {
  488.     D(PutStr( "UnLockDosList( LDF_DEVICES | LDF_READ );\n" ));
  489.     UnLockDosList( LDF_DEVICES | LDF_READ );
  490.     pdlList = 0;
  491.     }
  492.     if(bInhibited)
  493.     {
  494.     *pchDosDeviceColon = ':';
  495.     D(Printf( "Inhibit( \"%s\", DOSFALSE );\n", (ULONG)szDosDevice ));
  496.     (void) Inhibit( szDosDevice, DOSFALSE );
  497.     bInhibited = FALSE;
  498.     }
  499. }
  500.  
  501.  
  502. BOOL bGetExecDevice( BOOL bWillVerify )
  503. {
  504.     *pchDosDeviceColon = ':';
  505.     D(Printf( "Inhibit( \"%s\", DOSTRUE );\n", (ULONG)szDosDevice ));
  506.     if(!Inhibit( szDosDevice, DOSTRUE ))
  507.     {
  508.     /* This is a bit stupid, but compatible with v40 Format */
  509.     ReportErrSz( ertFailure, ERROR_OBJECT_WRONG_TYPE, 0 );
  510.     return FALSE;
  511.     }
  512.     bInhibited = TRUE;
  513.  
  514.     if( (pmpDiskIO = CreateMsgPort()) == 0
  515.     || (piosDisk = (struct IOStdReq *)
  516.         CreateIORequest( pmpDiskIO, sizeof(struct IOStdReq) )) == 0 )
  517.     {
  518.     ReportErrSz( ertFailure, ERROR_NO_FREE_STORE, 0 );
  519.     return FALSE;
  520.     }
  521.  
  522.     {
  523.     BYTE derr = OpenDevice( pszExecDevice,
  524.                 pfssm->fssm_Unit,
  525.                 (struct IORequest *)piosDisk,
  526.                 bDevfSet ? devfCurrent : pfssm->fssm_Flags );
  527.     if( derr != 0 )
  528.     {
  529.         ReportErrSz(
  530.         ertFailure, -1, "Device open returned error %ld", (ULONG)derr );
  531.         return FALSE;
  532.     }
  533.     bExecDevOpen = TRUE;
  534.     }
  535.  
  536.     {
  537.     /* This is an attempt to spot a scsi.device that is really an
  538.        IDE driver. If we have a card slot (A600/A1200) or AGA
  539.        (A1200/A4000/A4000T/CD32/clones) then it probably is.  If
  540.        the version number is lower than 40 then it does not limit
  541.        transfers to 128 blocks as required for correct operation
  542.        of some drives. */
  543.     struct GfxBase * GfxBase = (struct GfxBase *)
  544.         OpenLibrary( "graphics.library", 39 );
  545.     if( !strcmp( pszExecDevice, "scsi.device" )
  546.         && piosDisk->io_Device->dd_Library.lib_Version < 40
  547.         && (OpenResource("card.resource")
  548.         || (GfxBase && (GfxBase->ChipRevBits0 & GFXF_AA_ALICE)) ))
  549.     {
  550.         bSuspectIDE = TRUE;
  551.         if( pdenDevice->de_MaxTransfer > 0x10000 )
  552.         {
  553.         pdenDevice->de_MaxTransfer = 0x10000;
  554.         ReportErrSz(
  555.             ertWarning,
  556.             -1,
  557.             "This device appears to be on an IDE drive"
  558.             " using an old Commodore IDE driver.  This driver may"
  559.             " attempt to perform long data transfers that some IDE"
  560.             " drives do not carry out correctly.  To avoid the risk"
  561.             " of data loss, you should change this device's"
  562.             " MaxTransfer setting to 0x10000." );
  563.         }
  564.     }
  565.     else
  566.         bSuspectIDE = FALSE;
  567.     if(GfxBase)
  568.         CloseLibrary((struct Library *)GfxBase);
  569.     }
  570.  
  571.     if( ibyEnd > 0x100000000ULL )
  572.     {
  573.     static struct NSDeviceQueryResult nsdqr; /* must be in public memory */
  574.     BYTE derr;
  575.     BOOL bDoes64Bit = FALSE;
  576.  
  577.     piosDisk->io_Command    = NSCMD_DEVICEQUERY;
  578.     piosDisk->io_Data    = &nsdqr;
  579.     piosDisk->io_Length    = sizeof(nsdqr);
  580.     nsdqr.DevQueryFormat    = 0;
  581.     nsdqr.SizeAvailable     = 0;
  582.  
  583.     switch( derr = DoIO((struct IORequest *)piosDisk) )
  584.     {
  585.     case 0:
  586.         if( piosDisk->io_Actual == sizeof(nsdqr) )
  587.         if( nsdqr.DeviceType == NSDEVTYPE_TRACKDISK )
  588.         {
  589.             /* Look for 64-bit trackdisk commands - any one will
  590.                do, as drivers must implement all or none of them. */
  591.             UWORD * pcmd;
  592.             for( pcmd = nsdqr.SupportedCommands; *pcmd != 0; ++pcmd )
  593.             if( *pcmd == NSCMD_TD_READ64 )
  594.                 bDoes64Bit = TRUE;
  595.         }
  596.         else
  597.         {
  598.             /* How odd... it's not trackdisk-like */
  599.             ReportErrSz( ertError, ERROR_OBJECT_WRONG_TYPE, 0 );
  600.             return FALSE;
  601.         }
  602.         break;
  603.  
  604.     case IOERR_NOCMD:
  605.         break;
  606.  
  607.     default:
  608.         ReportErrSz(
  609.         ertFailure,
  610.         -1,
  611.         "Device query (NSCMD_DEVICEQUERY) returned error %ld",
  612.         (ULONG)derr );
  613.         return FALSE;
  614.     }
  615.  
  616.     if(!bDoes64Bit)
  617.     {
  618.         ReportErrSz(
  619.         ertError,
  620.         -1,
  621.         "This device extends beyond the first 4 gigabytes of the disk."
  622.         " In order to use this device, you must either use a driver"
  623.         " that supports the NSD 64-bit commands or install and run the"
  624.         " NSDPatch program." );
  625.         return FALSE;
  626.     }
  627.     }
  628.  
  629.     /* We need two buffers - one for writing and one for reading back to
  630.        verify the write */
  631.     if( (paulWriteBuffer = AllocMem( cbyCylinder,
  632.                      pdenDevice->de_BufMemType )) == 0
  633.     || (bWillVerify
  634.         && (paulReadBuffer = AllocMem( cbyCylinder,
  635.                        pdenDevice->de_BufMemType )) == 0) )
  636.     {
  637.     ReportErrSz( ertFailure, ERROR_NO_FREE_STORE, 0 );
  638.     return FALSE;
  639.     }
  640.  
  641.     {
  642.     /* Fill a buffer with junk, just like the official Format
  643.        does.  Perhaps this is meant to make the verification a
  644.        harder test. */
  645.     ULONG * pulFill = paulWriteBuffer;
  646.     ULONG * pulLimit = (ULONG *)((char *)paulWriteBuffer + cbyCylinder);
  647.     while( pulFill < pulLimit )
  648.     {
  649.         int i, j;
  650.         for( i = 0; i < 16 && pulFill < pulLimit; ++i )
  651.         {
  652.         ULONG ul = 0x444F5300 + ((i >> 2) << 10);
  653.         for( j = 0; j < 256 && pulFill < pulLimit; ++j )
  654.             *pulFill++ = ul++;
  655.         }
  656.     }
  657.     }
  658.  
  659.     /* For most drives, the geometry is totally fake and the drive can
  660.        never be formatted by the user.  The driver will really treat
  661.        TD_FORMAT the same as CMD_READ.  However, for some drives, we
  662.        will be doing a real format using the real geometry.  In that
  663.        case we *must* write a whole track at a time.  So what if
  664.        MaxTransfer is smaller than one track?
  665.  
  666.        * MaxTransfer happens to be useful for working round firmware
  667.          bugs in some IDE drives which scsi.device versions < 40
  668.          didn't work around, but that's not its purpose.  We've
  669.          already spotted problematic drivers and we know that they
  670.          don't really format the disk, so we should apply the limit
  671.      in that case.
  672.  
  673.        * As far as I can see, MaxTransfer is really there to limit the
  674.          time that the I/O bus may be tied up with DMA transfers.  If
  675.      we have to write more than that to format the disk, so be it.
  676.  
  677.        Ideally, we want to write whole cylinders at once, since longer
  678.        transfers are fastest.  We do this if the length of a cylinder
  679.        is less than or equal to MaxTransfer.
  680.        Otherwise, we write a track each time, unless we have a suspect
  681.        IDE drive and the length of a track is greater than MaxTransfer
  682.        - in which case we write at most MaxTransfer each time.  */
  683.  
  684.     cbyTransfer = (cbyCylinder <= pdenDevice->de_MaxTransfer) ?
  685.     cbyCylinder :
  686.     ((!bSuspectIDE
  687.       || cbyTrack <= pdenDevice->de_MaxTransfer) ?
  688.      cbyTrack :
  689.      pdenDevice->de_MaxTransfer);
  690.  
  691.     return TRUE;
  692. }
  693.  
  694.  
  695. void FreeExecDevice(void)
  696. {
  697.     if(paulWriteBuffer)
  698.     {
  699.     FreeMem( paulWriteBuffer, cbyCylinder );
  700.     paulWriteBuffer = 0;
  701.     }
  702.     if(paulReadBuffer)
  703.     {
  704.     FreeMem( paulReadBuffer, cbyCylinder );
  705.     paulReadBuffer = 0;
  706.     }
  707.     if(bExecDevOpen)
  708.     {
  709.     CloseDevice((struct IORequest *)piosDisk);
  710.     bExecDevOpen = FALSE;
  711.     }
  712.     if(piosDisk)
  713.     {
  714.     DeleteIORequest((struct IORequest *)piosDisk);
  715.     piosDisk = 0;
  716.     }
  717.     if(pmpDiskIO)
  718.     {
  719.     DeleteMsgPort(pmpDiskIO);
  720.     pmpDiskIO = 0;
  721.     }
  722. }
  723.  
  724.  
  725. BOOL bFormatCylinder( ULONG icyl )
  726. {
  727.     BYTE derr;
  728.  
  729.     /* put a "BAD\0" marker at the beginning in case format is aborted */
  730.     /* put normal "DOS\0" marker at the beginning of other cylinders */
  731.     paulWriteBuffer[0] =
  732.     (icyl == pdenDevice->de_LowCyl) ? 0x42414400 : 0x444F5300;
  733.  
  734.     if( !bTransferCylinder( paulWriteBuffer, icyl,
  735.                 NSCMD_TD_FORMAT64, TD_FORMAT ) )
  736.     return FALSE;
  737.  
  738.     /* Write out and invalidate the track buffer */
  739.     piosDisk->io_Command = CMD_UPDATE;
  740.     derr = DoIO((struct IORequest *)piosDisk);
  741.     if( derr == 0 )
  742.     {
  743.     piosDisk->io_Command = CMD_CLEAR;
  744.     derr = DoIO((struct IORequest *)piosDisk);
  745.     if( derr == 0 )
  746.         return TRUE;
  747.     }
  748.  
  749.     ReportErrSz( ertFailure, -1,
  750.          "Device %s returned error %ld",
  751.          (piosDisk->io_Command == CMD_UPDATE) ?
  752.          "flush (CMD_UPATE)" : "invalidate (CMD_CLEAR)",
  753.          (ULONG)derr );
  754.     return FALSE;
  755. }
  756.  
  757.  
  758. BOOL bVerifyCylinder( ULONG icyl )
  759. {
  760.     if( !bTransferCylinder( paulReadBuffer, icyl,
  761.                 NSCMD_TD_READ64, CMD_READ ) )
  762.     return FALSE;
  763.  
  764.     if( memcmp( paulWriteBuffer, paulReadBuffer, cbyCylinder ) != 0 )
  765.     {
  766.     ReportErrSz( ertFailure, -1, "Verify error" );
  767.     return FALSE;
  768.     }
  769.  
  770.     return TRUE;
  771. }
  772.  
  773.  
  774. static BOOL bTransferCylinder(
  775.     APTR pBuffer, ULONG icyl, UWORD cmd64, UWORD cmd32 )
  776. {
  777.     ULLONG ibyOffset = (ULLONG)icyl * (ULLONG)cbyCylinder;
  778.     ULLONG cbyLeft = cbyCylinder;
  779.  
  780.     do
  781.     {
  782.     ULONG cbyLength = (cbyTransfer <= cbyLeft) ? cbyTransfer : cbyLeft;
  783.  
  784.     if( ibyOffset + cbyLength >= 0x100000000ULL )
  785.     {
  786.         piosDisk->io_Command = cmd64;
  787.         piosDisk->io_Actual  = ibyOffset >> 32;
  788.     }
  789.     else
  790.         piosDisk->io_Command = cmd32;
  791.  
  792.     piosDisk->io_Offset      = ibyOffset;
  793.     piosDisk->io_Length      = cbyLength;
  794.     piosDisk->io_Data        = pBuffer;
  795.  
  796.     D(Printf( "DoIO() Cmd=%2lu Act=0x%08lx Len=0x%08lx "
  797.           "Data=0x%08lx Off=0x%08lx\n",
  798.           piosDisk->io_Command,
  799.           piosDisk->io_Actual,
  800.           piosDisk->io_Length,
  801.           (ULONG)piosDisk->io_Data,
  802.           piosDisk->io_Offset ));
  803.  
  804.     if( DoIO((struct IORequest *)piosDisk) != 0 )
  805.     {
  806.         const char * pszCommand;
  807.         switch(piosDisk->io_Command)
  808.         {
  809.         case NSCMD_TD_FORMAT64:
  810.         pszCommand = "format (NSCMD_TD_FORMAT64)"; break;
  811.         case TD_FORMAT:
  812.         pszCommand = "format (TD_FORMAT)"; break;
  813.         case NSCMD_TD_READ64:
  814.         pszCommand = "read (NSCMD_TD_READ64)"; break;
  815.         case CMD_READ:
  816.         pszCommand = "read (CMD_READ)"; break;
  817.         }
  818.         ReportErrSz( ertFailure, -1,
  819.              "Device %s returned error %ld",
  820.              pszCommand, (ULONG)piosDisk->io_Error );
  821.         return FALSE;
  822.     }
  823.  
  824.     ibyOffset += cbyLength;
  825.     pBuffer    = (char *)pBuffer + cbyLength;
  826.     cbyLeft   -= cbyLength;
  827.     }
  828.     while( cbyLeft != 0 );
  829.  
  830.     return TRUE;
  831. }
  832.  
  833.  
  834. BOOL bMakeFileSys( BOOL bFFS, BOOL bOFS, BOOL bIntl, BOOL bNoIntl,
  835.            BOOL bDirCache, BOOL bNoDirCache )
  836. {
  837.     if(!bFstSet)
  838.     {
  839.     fstCurrent = pdenDevice->de_DosType;
  840.  
  841.     if( fstCurrent >= 0x444F5300 && fstCurrent <= 0x444F5305 )
  842.     {
  843.         /* Adjust the file-system type according to command-line
  844.            switches or check-boxes (this exactly matches the logic of the
  845.            official version 40 Format command). */
  846.     
  847.         if(bFFS)        fstCurrent |= 1;
  848.         if(bOFS)        fstCurrent &= ~1;
  849.         if( !(fstCurrent & 2) )
  850.         {
  851.         if(bDirCache)    fstCurrent |= 4;
  852.         if(bNoDirCache)    fstCurrent &= ~4;
  853.         }
  854.         if( !(fstCurrent & 4) )
  855.         {
  856.         if(bIntl)    fstCurrent |= 2;
  857.         if(bNoIntl)    fstCurrent &= ~2;
  858.         }
  859.     } /* if( fstCurrent >= 0x444F5300 && fstCurrent <= 0x444F5305 ) */
  860.     } /* if(!bFstSet) */
  861.  
  862.     if(!bInhibited)
  863.     {
  864.     *pchDosDeviceColon = ':';
  865.     D(Printf( "Inhibit( \"%s\", DOSTRUE );\n", (ULONG)szDosDevice ));
  866.     if(!Inhibit( szDosDevice, DOSTRUE ))
  867.     {
  868.         /* This is a bit stupid, but compatible with v40 Format */
  869.         ReportErrSz( ertFailure, ERROR_OBJECT_WRONG_TYPE, 0 );
  870.         return FALSE;
  871.     }
  872.     bInhibited = TRUE;
  873.     }
  874.  
  875.     D(Printf( "Format( \"%s\", \"%s\", 0x%08lx );\n",
  876.           (ULONG)szDosDevice, (ULONG)(szVolume + 1), fstCurrent ));
  877.     if( !Format( szDosDevice,
  878.          (DOSBase->lib_Version == 36) ?
  879.          (char *)MKBADDR(szVolume) : szVolume + 1,
  880.          fstCurrent ) )
  881.     {
  882.     ReportErrSz( ertFailure, 0, 0 );
  883.     return FALSE;
  884.     }
  885.  
  886.     return TRUE;
  887. }
  888.  
  889.  
  890. BOOL bMakeFiles( BOOL bDiskIcon )
  891. {
  892.     struct Library * IconBase;
  893.     BOOL bSuccess = FALSE;
  894.  
  895.     /* We have to unlock the DOS list and uninhibit the device before we
  896.        can write files to the new volume! */
  897.     FreeDosDevice();
  898.  
  899.     if( (IconBase = OpenLibrary( "icon.library", 36 )) != 0 )
  900.     {
  901.     BPTR bpflRoot;
  902.     *pchDosDeviceColon = ':';
  903.     if( (bpflRoot = Lock( szDosDevice, SHARED_LOCK )) != 0 )
  904.     {
  905.         BPTR bpflOldCD = CurrentDir(bpflRoot);
  906.         BPTR bpflTrash = CreateDir("Trashcan");
  907.  
  908.         if(bpflTrash)
  909.         {
  910.         struct DiskObject * pdo;
  911.         UnLock(bpflTrash);
  912.         pdo = GetDefDiskObject(WBGARBAGE);
  913.  
  914.         if(pdo)
  915.         {
  916.             if( PutDiskObject( "Trashcan", pdo ) )
  917.             bSuccess = TRUE;
  918.             FreeDiskObject(pdo);
  919.         }
  920.         }
  921.  
  922.         if(!bSuccess)
  923.         ReportErrSz( ertFailure,
  924.                  -1,
  925.                  "Couldn't create trashcan on volume\n%s",
  926.                  (ULONG)(szVolume + 1) );
  927.         else if( bDiskIcon )
  928.         {
  929.         struct DiskObject * pdo;
  930.         char szDefDiskIcon[12+MAX_FS_NAME_LEN+4+1];
  931.  
  932.         bSuccess = FALSE; /* always assume the worst */
  933.  
  934.         /* Try to get the icon that DefIcons would use -
  935.            first, look for a device-specific one; if that
  936.            fails look for a file-system-specific one; if that
  937.            fails then get the standard default disk icon. */
  938.         *pchDosDeviceColon = 0;
  939.         RawDoFmtSz( szDefDiskIcon, "ENV:sys/def_%sdisk", szDosDevice );
  940.         if( (pdo = GetDiskObject(szDefDiskIcon)) == 0 )
  941.         {
  942.             int i;
  943.             strcpy( szDefDiskIcon, "ENV:sys/def_    disk" );
  944.             for( i = 0; i < 4; ++i )
  945.             {
  946.             char chFSType = ((char *)&fstCurrent)[i];
  947.             szDefDiskIcon[12+i] =
  948.                 (chFSType < 10) ? ('0' + chFSType) : chFSType;
  949.             }
  950.             if( (pdo = GetDiskObject(szDefDiskIcon)) == 0 )
  951.             pdo = GetDefDiskObject(WBDISK);
  952.         }
  953.         if(pdo)
  954.         {
  955.             if( PutDiskObject( "Disk", pdo ) )
  956.             bSuccess = TRUE;
  957.             FreeDiskObject(pdo);
  958.         }
  959.  
  960.         if(!bSuccess)
  961.             ReportErrSz( ertFailure,
  962.                  -1,
  963.                  "Couldn't create disk icon on volume\n%s",
  964.                  (ULONG)(szVolume + 1) );
  965.         }
  966.  
  967.         CurrentDir(bpflOldCD);
  968.         UnLock(bpflRoot);
  969.     }
  970.     CloseLibrary(IconBase);
  971.     }
  972.     else /* IconBase == 0 */
  973.     ReportErrSz( ertFailure, ERROR_INVALID_RESIDENT_LIBRARY, 0 );
  974.  
  975.     return bSuccess;
  976. }
  977.  
  978.  
  979. void FreeAll(void)
  980. {
  981.     FreeExecDevice();
  982.     FreeDosDevice();
  983. }
  984.  
  985.  
  986. static const UWORD AddChSz[] = {0x16C0, 0x4E75}; /* move.l d0,(a3)+ : rts */
  987.  
  988. void RawDoFmtSz( char * pszBuffer, const char * pszFormat, ... )
  989. {
  990.     RawDoFmt( (char *)pszFormat, (APTR)(&pszFormat+1),
  991.           (void (*)())AddChSz, pszBuffer );
  992. }
  993.  
  994. void RawDoVFmtSz( char * pszBuffer, const char * pszFormat, APTR pData )
  995. {
  996.     RawDoFmt( (char *)pszFormat, pData, (void (*)())AddChSz, pszBuffer );
  997. }
  998.